package cn.growthgate.fgo.core.shiro;

import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;

import cn.growthgate.fgo.entity.User;
import cn.growthgate.fgo.entity.enums.Role;
import cn.growthgate.fgo.entity.enums.UserStatus;
import cn.growthgate.fgo.exception.UsernamePasswordException;
import cn.growthgate.fgo.service.IUserRoleService;
import cn.growthgate.fgo.service.IUserService;
import cn.growthgate.fgo.util.NumberUtils;
import cn.growthgate.fgo.util.SpringUtils;
import cn.growthgate.fgo.util.StringUtils;

public class ShiroDbRealm extends AuthorizingRealm {

    /**
     * 认证回调函数,登录时调用.
     */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
			throws AuthenticationException {
		IUserService userService = SpringUtils.getBean(IUserService.class);
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = token.getUsername();
        String password = new String(token.getPassword());
        if (StringUtils.isBlank(username)) {
            throw new AuthenticationException("用户名不可以为空！");
        }
        User user = userService.getByUsernameAndPassword(username, password);
        if (user == null) {
            throw new UsernamePasswordException("用户名不存在或者密码错误！");
        }
        if (user.getStatus() == UserStatus.LOCKED) {
            throw new LockedAccountException("该用户已被锁定");
        }
        return new SimpleAuthenticationInfo(userService.transformSimpleUser(user), user.getPassword(), getName());
	}

    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
     */
	@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		IUserRoleService userRoleService = SpringUtils.getBean(IUserRoleService.class);
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		SimpleUser simpleUser = (SimpleUser) getAvailablePrincipal(principals);
		if (simpleUser == null) {
			return info;
		}
		List<Role> roles = userRoleService.getRoles(simpleUser.getId());
		if (CollectionUtils.isNotEmpty(roles)) {
			roles.forEach(role -> info.addRole(role.getName()));
		}
		return info;
	}

	/**
	 * 更新用户授权信息缓存.
	 */
	public void clearCachedAuthorizationInfo(User user) {
		Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
		if (cache != null) {
			for (Object key : cache.keys()) {
				if (key instanceof SimplePrincipalCollection) {
					SimplePrincipalCollection collection = (SimplePrincipalCollection) key;
					for (Object o : collection) {
						if (o instanceof SimpleUser) {
							SimpleUser simpleUser = (SimpleUser) o;
							if (NumberUtils.equals(simpleUser.getId(), user.getId())) {
								clearCachedAuthorizationInfo(collection);
								return;
							}
						}
					}
				}
			}
		}
	}

    /**
     * 清除所有用户授权信息缓存.
     */
    public void clearAllCachedAuthorizationInfo() {
        Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
        if (cache != null) {
            for (Object key : cache.keys()) {
                cache.remove(key);
            }
        }
    }

}
